package ru.gerunn.silent.server;

import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;

import org.apache.log4j.Logger;

import ru.gerunn.silent.exception.SilentReaderException;
import ru.gerunn.silent.exception.SilentServerSocketAcceptException;
import ru.gerunn.silent.exception.SilentServerSocketCreationException;
import ru.gerunn.silent.exception.SilentWriterException;
import ru.gerunn.silent.message.MessageQueue;

public class Server {

	public static Logger log = Logger.getLogger(Server.class);

	private final int port;
	private boolean logToConsole = false;
	private final ConcurrentHashMap<String, Session> sessions;
	private final MessageQueue messageQueue;
	private final SSLServerSocket socket;

	public Server() throws SilentServerSocketCreationException {
		this(8883);
	}

	public Server(final int port) throws SilentServerSocketCreationException {
		this.port = port;
		this.sessions = new ConcurrentHashMap<String, Session>();
		this.messageQueue = new MessageQueue();

		SSLServerSocketFactory serverSocketFactory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
		try {
			this.socket = (SSLServerSocket) serverSocketFactory.createServerSocket(getPort());
		} catch (IOException e) {
			throw new SilentServerSocketCreationException("error while creating server socket", e);
		}
	}

	public boolean isLogToConsole() {
		return logToConsole;
	}

	public void setLogToConsole(boolean logToConsole) {
		this.logToConsole = logToConsole;
	}

	public ConcurrentHashMap<String, Session> getSessions() {
		return sessions;
	}

	public void removeSession(String id) {
		this.sessions.remove(id);
	}

	public MessageQueue getMessageQueue() {
		return messageQueue;
	}

	public int getPort() {
		return port;
	}

	public SSLServerSocket getSocket() {
		return socket;
	}

	public void start() throws SilentServerSocketAcceptException {
		SSLSocket socket = null;
		ExecutorService pool = Executors.newCachedThreadPool();
		try {
			while ((socket = (SSLSocket) this.socket.accept()) != null) {
				log.info("got socket");
				stub(socket, pool);
			}
		} catch (IOException e) {
			throw new SilentServerSocketAcceptException("", e);
		}
	}

	public void stub(SSLSocket socket, ExecutorService pool) {
		try {
			Session session = new Session(socket);
			SessionCallable sc = new SessionCallable(session);
			Future<Object> future = pool.submit(sc);
			try {
				future.get();
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (ExecutionException e) {
				e.printStackTrace();
			}
		} catch (SilentReaderException e) {
			e.printStackTrace();
		} catch (SilentWriterException e) {
			e.printStackTrace();
		}
	}

}
